﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Core;
using Core.DBInterfaces;
using Core.Email;
using Core.Logging;
using Core.PageEntities;
using Core.PageInterfaces;
using LoggerService;
using Utils;
using Microsoft.Practices.ServiceLocation;
using System.Globalization;
using System.ComponentModel.DataAnnotations;

namespace Infrastructure
{
    public class PageManager : BaseManager
    {
        public SitePage GetSinglePageByGroupNameLang(string group, string name, string lang)
        {            
            var sitePage = _dbContext.Get<SitePage>(x => x.SitePageDefinition.SitePageGroup.GroupName.Equals(group, StringComparison.InvariantCultureIgnoreCase) &&
                x.SitePageDefinition.InternalName.Equals(name, StringComparison.InvariantCultureIgnoreCase) &&
                x.CultureInfo.Equals(lang, StringComparison.InvariantCultureIgnoreCase)).SingleOrDefault();

            return sitePage;
        }

        #region Administration back end

        /// <summary>
        /// Returns all site page groups
        /// </summary>
        /// <returns></returns>
        public IEnumerable<SitePageGroup> GetSitePageGroupsWithPagesAndContentBlocks()
        {
            _dbContext.IsCreateProxy = false;
            var sitePageGroups = (IEnumerable<SitePageGroup>)_dbContext.Get<SitePageGroup>(null, "SitePageDefinition.SitePage.ContentBlock.ContentBlockDefinition");
            return sitePageGroups;
        }
        /// <summary>
        /// Returns all site page groups, where pages lang == parap lang
        /// </summary>
        /// <returns></returns>
        public IEnumerable<SitePageGroup> GetSitePageGroupsWithPages(string uiLangName)
        {
            var sitePageGroups = (IEnumerable<SitePageGroup>)_dbContext.Get<SitePageGroup>();
            return sitePageGroups.Select(x => new SitePageGroup() { Id = x.Id, GroupName = x.GroupName, SitePageDefinition = x.SitePageDefinition.Select(y => new SitePageDefinition() { Id = y.Id, InternalName = y.InternalName, InternalDescription = y.InternalDescription, IsActive = y.IsActive, SitePageGroupId = y.SitePageGroupId, SitePageGroup = y.SitePageGroup, SitePage = y.SitePage.Where(z => z.CultureInfo == uiLangName).ToList() }).ToList() });
        }        
        /// <summary>
        /// Returns all site page definitions
        /// </summary>
        /// <returns></returns>
        public IEnumerable<SitePageDefinition> GetSitePageDefinitions()
        {
            _dbContext.IsCreateProxy = false;
            var sitePageDefinitions = (IEnumerable<SitePageDefinition>)_dbContext.Get<SitePageDefinition>();
            return sitePageDefinitions;
        }
        /// <summary>
        /// Returns all site pages
        /// </summary>
        /// <returns></returns>
        public IEnumerable<SitePage> GetSitePages()
        {
            var sitePages = (IEnumerable<SitePage>)_dbContext.Get<SitePage>();
            return sitePages;
        }
        /// <summary>
        /// Returns site pages with joined site page definitions and site page groups
        /// </summary>
        /// <returns></returns>
        public IEnumerable<SitePage> GetSitePagesWidthParentContainers(string lang)
        {
            var sitePages = (IEnumerable<SitePage>)_dbContext.Get<SitePage>(x => x.CultureInfo == lang, "SitePageDefinition.SitePageGroup");
            return sitePages;
        }
        /// <summary>
        /// Returns all content block definitions
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ContentBlockDefinition> GetContentBlockDefinitions()
        {
            _dbContext.IsCreateProxy = false;
            var contentBlockDefinitions = (IEnumerable<ContentBlockDefinition>)_dbContext.Get<ContentBlockDefinition>();
            return contentBlockDefinitions;
        }
        /// <summary>
        /// Returns all content blocks
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ContentBlock> GetContentBlocks()
        {
            var contentBlock = (IEnumerable<ContentBlock>)_dbContext.Get<ContentBlock>();
            return contentBlock;
        }

        /// <summary>
        /// Updates all site page groups, definitons, content blocks
        /// </summary>
        /// <param name="sitePageGroups"></param>
        /// <returns></returns>
        public string UpdateSitePageGroupsWithPages(List<SitePageGroup> sitePageGroups)
        {
            if (sitePageGroups == null)
                throw (new ArgumentNullException("SitePageGroups list is null"));

            var errorMessage = String.Empty;

            foreach (var sitePageGroup in sitePageGroups)
            {
                if (!sitePageGroup.IsValid)
                {
                    errorMessage += sitePageGroup.ValidationResultsString;
                }
            }

            if (!String.IsNullOrWhiteSpace(errorMessage))
                return errorMessage;

            var oldSitePageGroups = GetSitePageGroupsWithPagesAndContentBlocks().ToList();
            var oldSitePageDefinitions = GetSitePageDefinitions().ToList();

            //fix for .SelectMany<>
            foreach (var sitePageGroup in sitePageGroups)
            {
                if (sitePageGroup.SitePageDefinition == null)
                    sitePageGroup.SitePageDefinition = new List<SitePageDefinition>();
            }

            DeleteNotInListSitePageGroups(oldSitePageGroups, sitePageGroups);
            DeleteNotInListSitePageDefinitions(oldSitePageDefinitions, sitePageGroups.Where(x => x.Id != 0).SelectMany(x => x.SitePageDefinition));
            UpdateAllSitePageGroups(oldSitePageGroups, sitePageGroups);

            var count = _dbContext.Save();

            return String.Format("Success! {0} database changes saved.", count);
        }

        private void UpdateAllSitePageGroups(List<SitePageGroup> oldSitePageGroups, List<SitePageGroup> newSitePageGroups)
        {
            foreach (var newSitePageGroup in newSitePageGroups)
            {
                if (newSitePageGroup.Id != 0)
                {
                    UpdateSitePageGroup(oldSitePageGroups.SingleOrDefault(x => x.Id == newSitePageGroup.Id), newSitePageGroup);
                }
                else
                {
                    CreateSitePageGroup(newSitePageGroup);
                }
            }
        }

        private void DeleteNotInListSitePageGroups(List<SitePageGroup> oldSitePageGroups, List<SitePageGroup> sitePageGroups)
        {
            var Ids = sitePageGroups.Select(x => x.Id);
         
            var sitePageGroupsToDel = oldSitePageGroups.Where(x => 
                {
                    if (Ids.SingleOrDefault(y => y == x.Id) == 0 && !x.GroupName.Equals("root", StringComparison.InvariantCultureIgnoreCase))
                        return true;
                    return false;
                });

            foreach (var sitePageGroupToDel in sitePageGroupsToDel)
            {
                _dbContext.Delete<SitePageGroup>(sitePageGroupToDel);
            }
        }
        private void DeleteNotInListSitePageDefinitions(IEnumerable<SitePageDefinition> oldSitePageDefinitions, IEnumerable<SitePageDefinition> sitePageDefinitions)
        {
            var Ids = sitePageDefinitions.Select(x => x.Id);

            var sitePageDefinitionsToDel = oldSitePageDefinitions.Where(x =>
            {
                if (Ids.SingleOrDefault(y => y == x.Id) == 0 )
                    return true;
                return false;
            }).ToList();

            foreach (var sitePageDefinitionToDel in sitePageDefinitionsToDel)
            {
                _dbContext.Delete<SitePageDefinition>(sitePageDefinitionToDel);
            }
        } 

        private void UpdateSitePageGroup(SitePageGroup oldSitePageGroup, SitePageGroup newSitePageGroup)
        {

            if (oldSitePageGroup == null)
                throw new NullReferenceException("Can't find existed site page group");

            if (oldSitePageGroup.GroupName != newSitePageGroup.GroupName)
            {
                oldSitePageGroup.GroupName = newSitePageGroup.GroupName;
            }

            if (newSitePageGroup.SitePageDefinition == null)
                return;

            var allSitePageDefinition = newSitePageGroup.SitePageDefinition.ToList();

            foreach (var sitePageDefinition in allSitePageDefinition)
            {
                if (sitePageDefinition.Id != 0)
                {
                    UpdateSitePageDefinition(oldSitePageGroup, sitePageDefinition);
                }
                else
                {
                    CreateSitePageDefinition(oldSitePageGroup, sitePageDefinition);
                }
            }
        }

        private void CreateSitePageGroup(SitePageGroup newSitePageGroup)
        {
            SitePageGroup sitePageGroup = new SitePageGroup();
            sitePageGroup.GroupName = newSitePageGroup.GroupName;
           
            _dbContext.Insert<SitePageGroup>(sitePageGroup);

            if (newSitePageGroup.SitePageDefinition == null)
                return;

            foreach (var sitePageDefinition in newSitePageGroup.SitePageDefinition)
            {               
                CreateSitePageDefinition(sitePageGroup, sitePageDefinition);
            }
        }

        private void UpdateSitePageDefinition(SitePageGroup sitePageGroup, SitePageDefinition newSitePageDefinition)
        {
            var oldSitePageDefinition = GetSitePageDefinitions().ToList().SingleOrDefault(x => x.Id == newSitePageDefinition.Id);

            if (oldSitePageDefinition == null)
                throw new NullReferenceException("Can't find existed site page definition");


            if (oldSitePageDefinition.SitePageGroupId != sitePageGroup.Id)
            {
                oldSitePageDefinition.SitePageGroupId = sitePageGroup.Id;
            }

            if (oldSitePageDefinition.InternalDescription != newSitePageDefinition.InternalDescription)
            {
                oldSitePageDefinition.InternalDescription = newSitePageDefinition.InternalDescription;
            }

            if (oldSitePageDefinition.InternalName != newSitePageDefinition.InternalName)
            {
                oldSitePageDefinition.InternalName = newSitePageDefinition.InternalName;
            }

            if (oldSitePageDefinition.IsActive != newSitePageDefinition.IsActive)
            {
                oldSitePageDefinition.IsActive = newSitePageDefinition.IsActive;
            }

            if (oldSitePageDefinition.SortOrder != newSitePageDefinition.SortOrder)
            {
                oldSitePageDefinition.SortOrder = newSitePageDefinition.SortOrder;
            }

            foreach (var sitePage in newSitePageDefinition.SitePage)
            {
                if (sitePage.Id != 0)
                {
                    UpdateSitePage(sitePage);
                }
                else
                {
                    CreateSitePage(oldSitePageDefinition, sitePage);
                }
            }
        }

        private void CreateSitePageDefinition(SitePageGroup sitePageGroup, SitePageDefinition newSitePageDefinition)
        {
            SitePageDefinition sitePageDefinition = new SitePageDefinition();
            sitePageDefinition.InternalDescription = newSitePageDefinition.InternalDescription;
            sitePageDefinition.InternalName = newSitePageDefinition.InternalName;
            sitePageDefinition.IsActive = newSitePageDefinition.IsActive;
            sitePageDefinition.SortOrder = newSitePageDefinition.SortOrder;
            if (sitePageGroup.SitePageDefinition == null)
                sitePageGroup.SitePageDefinition = new List<SitePageDefinition>();

            sitePageGroup.SitePageDefinition.Add(sitePageDefinition);

            if (newSitePageDefinition.SitePage == null)
                return;

            foreach (var sitePage in newSitePageDefinition.SitePage)
            {
                CreateSitePage(sitePageDefinition, sitePage);
            }            
        }

        private void UpdateSitePage(SitePage newSitePage)
        {
            var oldSitePage = GetSitePages().ToList().SingleOrDefault(x => x.Id == newSitePage.Id);

            if (oldSitePage == null)
                throw new NullReferenceException("Can't find existed site page");

            if (oldSitePage.Title != newSitePage.Title)
            {
                oldSitePage.Title = newSitePage.Title;
            }

            if (oldSitePage.MetaKeywords != newSitePage.MetaKeywords)
            {
                oldSitePage.MetaKeywords = newSitePage.MetaKeywords;
            }

            if (oldSitePage.MetaName != newSitePage.MetaName)
            {
                oldSitePage.MetaName = newSitePage.MetaName;
            }

            foreach (var contentBlock in newSitePage.ContentBlock)
            {
                //old content blocks have there real Id > 0 : 1,2,3 e.t.c, new have Id < 0
                //-1,-2,-3
                if (contentBlock.Id > 0)
                {
                    UpdateContentBlock(contentBlock);
                }
                else
                {
                    CreateContentBlock(oldSitePage, contentBlock);
                }
            }
        }

        private void CreateSitePage(SitePageDefinition sitePageDefinition, SitePage newSitePage)
        {
            SitePage sitePage = new SitePage();
            sitePage.Title = newSitePage.Title;
            sitePage.MetaName = newSitePage.MetaName;
            sitePage.MetaKeywords = newSitePage.MetaKeywords;
            sitePage.CultureInfo = newSitePage.CultureInfo;

            if (sitePageDefinition.SitePage == null)
                sitePageDefinition.SitePage = new List<SitePage>();
            sitePageDefinition.SitePage.Add(sitePage);

            if (newSitePage.ContentBlock == null)
                return;

            foreach (var contentBlock in newSitePage.ContentBlock)
            {
                CreateContentBlock(sitePage, contentBlock);
            }
        }

        private void UpdateContentBlock(ContentBlock newContentBlock)
        {
            var oldContentBlock = GetContentBlocks().ToList().SingleOrDefault(x => x.Id == newContentBlock.Id);

            if (oldContentBlock == null)
                throw new NullReferenceException("Can't find existed content block");

            if (oldContentBlock.HTMLContent != newContentBlock.HTMLContent)
            {
                oldContentBlock.HTMLContent = newContentBlock.HTMLContent;
            }
        }

        private void CreateContentBlock(SitePage sitePage, ContentBlock newContentBlock)
        {
            ContentBlock contentBlock = new ContentBlock();
            contentBlock.HTMLContent = newContentBlock.HTMLContent;
            contentBlock.ContentBlockDefinitionId = newContentBlock.ContentBlockDefinitionId;
            if (sitePage.ContentBlock == null)
                sitePage.ContentBlock = new List<ContentBlock>();
            sitePage.ContentBlock.Add(contentBlock);
        }

        #endregion
    }
}
